package com.ying.tests.log;

import cn.hutool.json.JSONUtil;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.*;
import org.aspectj.lang.reflect.MethodSignature;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Component;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;
import org.springframework.web.multipart.MultipartFile;
import javax.servlet.http.HttpServletRequest;
import java.util.HashMap;
import java.util.Map;

/**
 * 日志切面
 */
@Component
@Aspect
public class RequestLogAspect {

    private static final Logger logger = LoggerFactory.getLogger(RequestLogAspect.class);

    /**
     * 切面的使用【基于注解】
     * @Aspect => 声明该类为一个注解类
     * 切点注解：
     * @Pointcut => 定义一个切点，可以简化代码
     * 通知注解：
     * 1.@Before => 在切点之前执行代码
     * 2.@After => 在切点之后执行代码
     * 3.@AfterReturning => 切点返回内容后执行代码，可以对切点的返回值进行封装
     * 4.@AfterThrowing => 切点抛出异常后执行
     * 5.@Around => 环绕，在切点前后执行代码
     */
    @Pointcut("execution(* com.ying.tests.controller..*(..))")
    public void requestServer() {

    }

    @Before("requestServer()")
    public void doBefore(JoinPoint joinPoint) {
        // 获取请求上下文
        ServletRequestAttributes attributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
        // 获取请求request
        HttpServletRequest request = attributes.getRequest();
        logger.info("===============================Start===============================");
        logger.info("IP                     : {}", request.getRemoteAddr());
        logger.info("URL                    : {}", request.getRequestURL().toString());
        logger.info("HTTP Method            : {}", request.getMethod());
        logger.info("Class Method           : {}.{}", joinPoint.getSignature().getDeclaringTypeName(), joinPoint.getSignature().getName());
    }

    @Around("requestServer()")
    public Object doAround(ProceedingJoinPoint proceedingJoinPoint) throws Throwable {
        // 获取请求上下文
        ServletRequestAttributes attributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
        // 获取请求request
        HttpServletRequest request = attributes.getRequest();
        // 获取请求开始时间
        long start = System.currentTimeMillis();
        // 获取响应参数
        Object result = proceedingJoinPoint.proceed();
        RequestLogInfo requestLogInfo = new RequestLogInfo();
        // 存储请求IP
        requestLogInfo.setIp(request.getRemoteAddr());
        // 存储请求地址
        requestLogInfo.setUrl(request.getRequestURL().toString());
        // 存储请求方法
        requestLogInfo.setHttpMethod(request.getMethod());
        // 存储方法包路径
        requestLogInfo.setClassMethod(String.format("%s.%s", proceedingJoinPoint.getSignature().getDeclaringTypeName(), proceedingJoinPoint.getSignature().getName()));
        // 存储请求参数
        requestLogInfo.setRequestParams(getRequestParamsByProceedingJoinPoint(proceedingJoinPoint));
        // 存储响应参数
        requestLogInfo.setResponseParams(result);
        // 存储响应时长
        requestLogInfo.setTimeCost(System.currentTimeMillis() - start);
        logger.info("Request Log Info       : {}", JSONUtil.toJsonStr(requestLogInfo));
        return result;
    }

    @After("requestServer()")
    public void doAfter(JoinPoint joinPoint) {
        logger.info("===============================End=================================");
    }

    @AfterThrowing(pointcut = "requestServer()", throwing = "e")
    public void doAfterThrowing(JoinPoint joinPoint, RuntimeException e) {
        // 获取请求上下文
        ServletRequestAttributes attributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
        // 获取请求request
        HttpServletRequest request = attributes.getRequest();
        RequestLogErrorInfo requestLogErrorInfo = new RequestLogErrorInfo();
        requestLogErrorInfo.setIp(request.getRemoteAddr());
        requestLogErrorInfo.setUrl(request.getRequestURL().toString());
        requestLogErrorInfo.setHttpMethod(request.getMethod());
        requestLogErrorInfo.setClassMethod(String.format("%s.%s", joinPoint.getSignature().getDeclaringTypeName(), joinPoint.getSignature().getName()));
        requestLogErrorInfo.setRequestParams(getRequestParamsByJoinPoint(joinPoint));
        requestLogErrorInfo.setException(e);
        logger.info("Request Log Error Info : {}", JSONUtil.toJsonStr(requestLogErrorInfo));
    }

    /**
     * 获取入参
     * @param proceedingJoinPoint
     */
    private Map<String, Object> getRequestParamsByProceedingJoinPoint(ProceedingJoinPoint proceedingJoinPoint) {
        //参数名
        String[] paramNames = ((MethodSignature)proceedingJoinPoint.getSignature()).getParameterNames();
        //参数值
        Object[] paramValues = proceedingJoinPoint.getArgs();
        return buildRequestParam(paramNames, paramValues);
    }
    /**
     * 获取入参
     * @param joinPoint
     */
    private Map<String, Object> getRequestParamsByJoinPoint(JoinPoint joinPoint) {
        //参数名
        String[] paramNames = ((MethodSignature)joinPoint.getSignature()).getParameterNames();
        //参数值
        Object[] paramValues = joinPoint.getArgs();
        return buildRequestParam(paramNames, paramValues);
    }
    private Map<String, Object> buildRequestParam(String[] paramNames, Object[] paramValues) {
        Map<String, Object> requestParams = new HashMap<>();
        for (int i = 0; i < paramNames.length; i++) {
            Object value = paramValues[i];
            //如果是文件对象
            if (value instanceof MultipartFile) {
                MultipartFile file = (MultipartFile) value;
                value = file.getOriginalFilename();  //获取文件名
            }
            requestParams.put(paramNames[i], value);
        }
        return requestParams;
    }

}
